package com.dm.cloud.utils.distributedlock;

import com.dm.cloud.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 基于Redis的分布式锁
 * 使用后手动清除或者等待超时失效
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "custom.dslock.redis", name = "enable", havingValue = "true")
public class RedisDSLock {

    protected static long INTERNAL_LOCK_LEASE_TIME = 3000;

    //过期时长设置
    private static SetParams params = SetParams.setParams().nx().px(INTERNAL_LOCK_LEASE_TIME);

    //jedis配置
    static JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();

    static JedisPool jedisPool ;


    //初始化Jedis
    private static void initJedis(){
        if(null == jedisPool){

            //最带空闲数
            int maxIdle = 50;
            //最大连接数
            int maxTotal = 100;
            //最长等待时间
            int maxWaitMilis = 3000;

            String ip = "127.0.0.1";
            int port = 6379;

            //读取配置
            try {

                maxIdle = SpringContextUtils.containProperty("custom.dslock.redis.maxIdle",true)?
                        (int) SpringContextUtils.getProperty("custom.dslock.redis.maxIdle"):maxIdle;

                maxTotal = SpringContextUtils.containProperty("custom.dslock.redis.maxTotal",true)?
                        (int) SpringContextUtils.getProperty("custom.dslock.redis.maxTotal"):maxTotal;

                maxWaitMilis = SpringContextUtils.containProperty("custom.dslock.redis.maxWaitMilis",true)?
                        (int) SpringContextUtils.getProperty("custom.dslock.redis.maxWaitMilis"):maxWaitMilis;

                if(SpringContextUtils.containProperty("custom.dslock.redis.ip",true)){
                    ip = SpringContextUtils.getProperty("custom.dslock.redis.ip").toString();
                }else {
                    log.warn("cannot get the property [ custom.dslock.redis.ip ] for redis ip, using default ip [ 127.0.0.1 ] ");
                }

                if(SpringContextUtils.containProperty("custom.dslock.redis.port",true)){
                    port = (int) SpringContextUtils.getProperty("custom.dslock.redis.port");
                }else{
                    log.warn("cannot get the property [ custom.dslock.redis.port ] for redis port, using default port [ 6379 ] ");
                }
            }catch (Exception ex){
                log.error("initJedis error",ex.getMessage());
            }
            //设置最大空闲数
            jedisPoolConfig.setMaxIdle(maxIdle);
            //最大连接数
            jedisPoolConfig.setMaxTotal(maxTotal);
            //最大等待毫秒数
            jedisPoolConfig.setMaxWaitMillis(maxWaitMilis);

            jedisPool = new JedisPool(jedisPoolConfig,ip, port);
        }
    }

    /**
     * 申请锁
     * @param key  主键
     * @param value 值
     * @return
     */
    public static boolean tryLockForever(String key, String value) {
        initJedis();
        try( Jedis jedis = jedisPool.getResource()){
            String lock = jedis.set(key, value);
            if ("OK".equals(lock)) {
                return true;
            }else{
                return false;
            }
        }
    }

    /**
     * 申请锁
     * @param key  主键
     * @param value 值
     * @return
     */
    public static boolean tryLockForever(String key, String value,long waitTime) {
        initJedis();
        Long start = System.currentTimeMillis();
        for (; ; ) {

            if(System.currentTimeMillis()-start>waitTime){
                log.error("wait for lock out of time");
                return false;
            }

            if(tryLockForever(key, value))
                return true;
        }
    }

    /**
     * 申请锁
     * @param key  主键
     * @param value 值
     * @return
     */
    public static boolean tryLock(String key, String value) {
        initJedis();
        try( Jedis jedis = jedisPool.getResource()){
            String lock = jedis.set(key, value,params);
            if ("OK".equals(lock)) {
                return true;
            }else{
                return false;
            }
        }
    }

    /**
     * 申请并等待锁
     * @param key  主键
     * @param value 值
     * @param waitTime 等待时长 毫秒
     * @return
     */
    public static boolean tryLock(String key, String value,long waitTime) {
        initJedis();
        Long start = System.currentTimeMillis();
        for (; ; ) {

            if(System.currentTimeMillis()-start>waitTime){
                log.error("wait for lock out of time");
                return false;
            }

            if(tryLock(key, value))
                return true;
        }
    }

    /**
     * 释放锁
     * @param key   主键
     * @param value 值
     */
    public static boolean unLock(String key, String value) {
        initJedis();
        try(Jedis jedis = jedisPool.getResource()){

            String script =
                    "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                            "   return redis.call('del',KEYS[1]) " +
                            "else" +
                            "   return 0 " +
                            "end";
            try {
                String result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value)).toString();
                return "1".equals(result) ? true : false;
            } finally {
                jedis.close();
            }
        }
    }


    /**
     * 获取符合条件的key值
     */
    public static Set<String> keys(String pattern) {
        initJedis();
        try(Jedis jedis = jedisPool.getResource()){
            try {
                return jedis.keys(pattern);
            } finally {
                jedis.close();
            }
        }
    }
}
